package cn.bluethink.timescaledb.service.impl;

import cn.bluethink.timescaledb.entity.ResponseDTO;
import cn.bluethink.timescaledb.filter.QueryFilter;
import cn.bluethink.timescaledb.mapper.DBHelpMapper;
import cn.bluethink.timescaledb.mapper.TimeScaleDynamicRowMapper;
import cn.bluethink.timescaledb.service.TimeScaleDynamicService;
import cn.bluethink.timescaledb.util.DBUtil;
import cn.bluethink.timescaledb.util.PageUtil;
import onegis.filter.GFilter;
import onegis.psde.attribute.Field;
import onegis.psde.attribute.Fields;
import onegis.psde.dynamicdata.DynamicData;
import onegis.psde.dynamicdata.DynamicDatas;
import onegis.psde.dynamicdata.ObjectDynamicData;
import onegis.psde.dynamicdata.ObjectDynamicDatas;
import onegis.psde.psdm.OType;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.sql.SQLException;
import java.util.*;

@Service
public class TimeScaleDynamicServiceImpl implements TimeScaleDynamicService {

    public static final Integer DEFAULT_INDEX = 0;
    public static final Integer DEFAULT_PAGE_SIZE = 5000;
    public static final Integer DEFAULT_PAGE_NUM = 1;

    //private FilterFactory2 filterFactory = (FilterFactory2) CommonFactoryFinder.getFilterFactory(GeoTools.getDefaultHints());

    Logger logger = LoggerFactory.getLogger(TimeScaleDynamicServiceImpl.class);

    @Autowired
    DBHelpMapper dbHelpMapper;

    @Autowired
    JdbcTemplate jdbcTemplate;

    /**
     * @param dynamicDatas
     * @return
     */
    @Transactional(rollbackFor = {RuntimeException.class, SQLException.class})
    @Override
    public ResponseDTO save(DynamicDatas dynamicDatas) {
        ResponseDTO responseDTO = new ResponseDTO();
        List<ObjectDynamicDatas> objectDynamicDatasList = dynamicDatas.getObjectDynamicDatasList();
        OType oType = objectDynamicDatasList.get(DEFAULT_INDEX).getOtype();
        String typeName = oType.getTags();
        LinkedHashMap<String, String> columns = getColumns(oType.getFields());
        if (dbHelpMapper.tableIsExit(typeName) == 0) {
            createTable(typeName, columns);
            responseDTO = save(objectDynamicDatasList.get(DEFAULT_INDEX).getObjectDynamicDataList(), typeName, columns.keySet());
        } else {
            List<String> tableColumns = dbHelpMapper.getColumnNamesByTableName(typeName);
            if (DBUtil.whetherInsert(tableColumns, columns.keySet(), false)) {
                responseDTO = save(objectDynamicDatasList.get(DEFAULT_INDEX).getObjectDynamicDataList(), typeName, tableColumns);
            } else {
                responseDTO.setMessage("上传失败,请校验数据格式或更改表名.");
            }
        }
        return responseDTO;
    }

    /**
     * @param formatFilter
     * @param feign
     * @param b
     * @param queryFilter
     * @return
     * @throws Exception
     */
    @Override
    public ResponseDTO query(GFilter formatFilter, String feign, boolean b, QueryFilter queryFilter) {

        String tableName = queryFilter.getgFilter().getTableName();
        if (StringUtils.isEmpty(tableName)) {
            return new ResponseDTO(null,"tableName is empty!",new Date());
        }
        if (dbHelpMapper.tableIsExit(tableName) == 0) {
            return new ResponseDTO(null,"table is not exit!",new Date());
        }

        List<String> columns = dbHelpMapper.getColumnNamesByTableName(tableName);
        StringBuilder selectColumns = new StringBuilder();
        for (String column : columns) {
            selectColumns.append("\"" + column).append("\"").append(",");
        }
        selectColumns.deleteCharAt(selectColumns.length() - 1);
        String screenSql = DBUtil.buildWhereSql(formatFilter);
        String sql = StringUtils.isEmpty(screenSql) ? "select " + selectColumns + " from " + tableName : "select " + selectColumns + " from " + tableName + " where " + " " + screenSql;
        if (queryFilter.getP() == null || queryFilter.getP() <= 0){
            queryFilter.setP(DEFAULT_PAGE_NUM);
        }
        if (queryFilter.getPageSize() ==null || queryFilter.getPageSize() <= 0){
            queryFilter.setPageSize(DEFAULT_PAGE_SIZE);
        }
        PageUtil<ObjectDynamicDatas> pageUtil = new PageUtil<>(dbHelpMapper.getCountByTableName(tableName),queryFilter.getP(),queryFilter.getPageSize());
        sql = sql + " LIMIT " + pageUtil.getPageSize() + " OFFSET " + pageUtil.getStart() ;
        List<ObjectDynamicDatas> result = jdbcTemplate.query(sql, new TimeScaleDynamicRowMapper(columns));

        DynamicDatas dynamicDatas = new DynamicDatas();
        dynamicDatas.setObjectDynamicDatasList(result);
        return new ResponseDTO(dynamicDatas, "success", new Date());
    }

    /**
     * @param objectDynamicDataList
     * @return
     */
    private ResponseDTO save(List<ObjectDynamicData> objectDynamicDataList, String tableName, Collection<String> columns) {
        List<LinkedHashMap<String, Object>> allData = new ArrayList<>();
        Long oId = objectDynamicDataList.get(DEFAULT_INDEX).getOid();
        int saveCount = objectDynamicDataList.size();
        for (ObjectDynamicData objectDynamicData : objectDynamicDataList) {
            LinkedHashMap<String, Object> data = new LinkedHashMap<>();
            data.put("oid", oId);
            data.put("startTime", objectDynamicData.getStartTime());
            data.put("endTime", objectDynamicData.getEndTime());
            List<DynamicData> dynamicDataList = objectDynamicData.getDynamicData();
            for (DynamicData dynamicData : dynamicDataList) {
                data.put("x", dynamicData.getLocation().getX());
                data.put("y", dynamicData.getLocation().getY());
                data.put("z", dynamicData.getLocation().getZ());
                data.put("currentTime", dynamicData.getCurrentTime());
                List<DynamicData.Attribute> attributes = dynamicData.getAttributes();
                for (DynamicData.Attribute attribute : attributes) {
                    data.put(attribute.getName(), attribute.getValue());
                }
            }
            allData.add(data);
        }

        if (CollectionUtils.isEmpty(columns)) {
            columns = dbHelpMapper.getColumnNamesByTableName(tableName);
        }

        ArrayList<LinkedHashMap<String, Object>> dataInOrder = new ArrayList<>();
        for (LinkedHashMap<String, Object> data : allData) {
            LinkedHashMap<String, Object> dataMapInOrder = new LinkedHashMap<>();
            for (String column : columns) {
                dataMapInOrder.put(column, data.get(column));
            }
            dataInOrder.add(dataMapInOrder);
        }

        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("data", dataInOrder);
        paramMap.put("tableName", tableName);
        paramMap.put("columns", columns);
        dbHelpMapper.insertDynamicData(paramMap);
        logger.info("本次存储" + saveCount + "条数据.");
        return new ResponseDTO(null, "success", new Date());
    }


    /**
     * @param tableName 表名
     * @param columns   表列名
     */
    private void createTable(String tableName, Map<String, String> columns) {
        dbHelpMapper.createTableByOType(tableName, columns);
        dbHelpMapper.createHyperTable(tableName, "currentTime");
    }

    /**
     * 此处构建得到的Map中已保证存在oid、currentTime等公共属性
     * @param fields
     * @return Map<String, String>key-列名 value-列类型
     */
    private static LinkedHashMap<String, String> getColumns(Fields fields) {
        LinkedHashMap<String, String> columns = new LinkedHashMap<>();
        List<Field> fieldList = fields.getFields();
        for (Field field : fieldList) {
            String name = field.getName();
            int value = field.getType().getValue();
            String type = "varchar";
            if (value == 6) {
                type = "timestamptz";
            }

            if (value == 7 || value == 2) {
                type = "int8";
            }

            if (value == 3) {
                type = "float4";
            }

            if (value == 4) {
                type = "float8";
            }

            columns.put(name, type);
        }

        if (StringUtils.isEmpty(columns.get("currentTime"))) {
            columns.put("currentTime", "timestamptz");
        }
        if (StringUtils.isEmpty(columns.get("x"))) {
            columns.put("x", "varchar");
        }
        if (StringUtils.isEmpty(columns.get("y"))) {
            columns.put("y", "varchar");
        }
        if (StringUtils.isEmpty(columns.get("z"))) {
            columns.put("z", "varchar");
        }
        if (StringUtils.isEmpty(columns.get("oid"))) {
            columns.put("oid", "int8");
        }
        if (StringUtils.isEmpty(columns.get("startTime"))) {
            columns.put("startTime", "timestamptz");
        }
        if (StringUtils.isEmpty(columns.get("endTime"))) {
            columns.put("endTime", "timestamptz");
        }
        return columns;
    }

}
